1

时间:2017年12月01日星期五
说明:本文部分内容均来自慕课网。@慕课网:http://www.imooc.com
教学源码:无
学习源码:https://github.com/zccodere/s...

第一章:课程介绍

1-1 课程介绍

热部署的使用场景

本地调式
线上发布

热部署的使用优点

无论本地还是线上,都适用
无需重启服务器:提高开发、调式效率、提升发布、运维效率、降低运维成本

前置知识

掌握Java语言
有一定的Spring开发经验
掌握构建Spring Boot项目的方法

课程提纲

原理解析
案例分析
项目演示
测试验证
发布程序
课程总结

第二章:原理解析

2-1 部署加载

Java热部署与热加载联系

不重启服务器编译或部署项目
基于Java的类加载器实现

Java热部署与热加载的区别

部署方式
--热部署在服务器运行时重新部署项目
--热加载在运行时重新加载class
实现原理
--热部署直接重新加载整个应用
--热加载在运行时重新加载class
使用场景
--热部署更多的是在生产环境使用
--热加载则更多的是在开发环境使用

2-2 原理解析

Java类的加载过程

clipboard.png

类加载的五个阶段

clipboard.png

Java类加载器特点

1.由AppClassLoader(系统类加载器)开始加载指定的类
2.类加载器将加载任务交给其父类,如果其父类找不到,再由自己去加载
3.BootstrapLoader(启动类加载器)是最顶级的类加载器

Java类的热部署

类的热加载
配置Tomcat

通过类的热加载实现热部署

clipboard.png

通过配置Tomcat实现热部署

1.直接把项目web文件夹放在webapps里
2.在tomcat/conf/server.xml中的<host></host>内部添加<context/>标签
3.在%tomcat_home%/conf/Catalina/localhost中添加一个XML

第三章:案例分析

3-1 案例介绍

写一个Java类热加载的实际案例,要求如下

1.类层次结构清晰,修改某一个Java类文件不需要重启服务或者重新编译运行程序
2.可适当的运用一些设计模式使代码结构更加清晰明了,比如工厂模式等

3-2 案例实现

创建名为classloader的gradle工程build.gradle脚本如下

apply plugin: 'java'
apply plugin: 'eclipse'

group = 'com.myimooc'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
    maven{ url "http://maven.aliyun.com/nexus/content/groups/public/"}
    mavenCentral()
}

dependencies {

}

代码编写

1.编写MyClassLoader类

package com.myimooc.classloader;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;

/**
 * @title 自定义Java类加载器
 * @describe 来实现Java类的热加载
 * @author zc
 * @version 1.0 2017-12-01
 */
public class MyClassLoader extends ClassLoader{
    
    /** 要加载的Java类的classpath路径 */
    private String classpath;
    
    public MyClassLoader(String classpath) {
        super(ClassLoader.getSystemClassLoader());
        this.classpath = classpath;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        
        byte[] data = this.loadClassData(name);
        return this.defineClass(name, data, 0, data.length);
    }
    
    /**
     * @title 加载class文件中的内容
     * @describe 加载class文件中的内容
     * @author zc
     * @version 1.0 2017-12-01
     */
    private byte[] loadClassData(String name) {
        try{
            name = name.replace(".", "//");
            FileInputStream is = new FileInputStream(new File(this.classpath + name +".class"));
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            int b = 0;
            while((b = is.read()) != -1){
                baos.write(b);
            }
            is.close();
            return baos.toByteArray();
        }catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

2.编写BaseManager类

package com.myimooc.classloader;

/**
 * @title 标识接口
 * @describe 实现这个接口的子类需要动态更新
 * @author zc
 * @version 1.0 2017-12-01
 */
public interface BaseManager {
    
    public void logic();
    
}

3.编写MyManager类

package com.myimooc.classloader;

/**
 * @title 接口实现类
 * @describe BaseManager的子类,此类需要实现Java类的热加载功能
 * @author zc
 * @version 1.0 2017-12-01
 */
public class MyManager implements BaseManager {

    @Override
    public void logic() {
        System.out.println("学习如何实现Java类的热加载案例");
    }
}

4.编写LoadInfo类

package com.myimooc.classloader;

/**
 * @title 类加载信息
 * @describe 封装加载类的信息
 * @author zc
 * @version 1.0 2017-12-01
 */
public class LoadInfo {
    
    /** 自定义的类加载器 */
    private MyClassLoader myLoader;
    /** 记录要加载类的时间戳,加载的时间 */
    private long loadTime;
    
    private BaseManager manager;

    public LoadInfo(MyClassLoader myLoader, long loadTime) {
        super();
        this.myLoader = myLoader;
        this.loadTime = loadTime;
    }

    public MyClassLoader getMyLoader() {
        return myLoader;
    }

    public void setMyLoader(MyClassLoader myLoader) {
        this.myLoader = myLoader;
    }

    public long getLoadTime() {
        return loadTime;
    }

    public void setLoadTime(long loadTime) {
        this.loadTime = loadTime;
    }

    public BaseManager getManager() {
        return manager;
    }

    public void setManager(BaseManager manager) {
        this.manager = manager;
    }
}

5.编写ManagerFactory类

package com.myimooc.classloader;

import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

/**
 * @title Manager工厂类
 * @describe 加载manager的工厂
 * @author zc
 * @version 1.0 2017-12-01
 */
public class ManagerFactory {
    
    /** 记录热加载类的加载信息 */
    private static final Map<String,LoadInfo> loadTimeMap = new HashMap<String,LoadInfo>();
    
    /** 要加载的类的classpath路径 */
    public static final String CLASS_PATH = "D:/AllSpace/ByStudy/classloader/bin/";
    
    /** 实现热加载的类的全名称(包名+类名) */
    public static final String MY_MANAGER = "com.myimooc.classloader.MyManager";
    
    public static BaseManager getManager(String className){
        File loadFile = new File(CLASS_PATH + className.replaceAll("\\.", "/")+".class");
        long lastModified = loadFile.lastModified();

        if(loadTimeMap.get(className)== null){
            // loadTimeMap不包含className为key的LoadInfo信息。
            // 证明这个类没有被加载,那么需要加载这个类到JVM中
            load(className,lastModified);
            
        }else if(loadTimeMap.get(className).getLoadTime()!=lastModified){
            // 加载类的时间戳变化了,同样要重新加载
            load(className,lastModified);
        }
        
        return loadTimeMap.get(className).getManager();
    }

    private static void load(String className, long lastModified) {
        MyClassLoader myClassLoader = new MyClassLoader(CLASS_PATH);
        Class<?> loadClass = null;
        try {
            loadClass = myClassLoader.findClass(className);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        BaseManager manager = newInstance(loadClass);
        LoadInfo loadInfo = new LoadInfo(myClassLoader,lastModified);
        loadInfo.setManager(manager);
        loadTimeMap.put(className, loadInfo);
    }

    /**
     * @title 创建实例对象
     * @describe 以反射的方式创建BaseManager子类对象
     * @author zc
     * @version 1.0 2017-12-01
     */
    private static BaseManager newInstance(Class<?> loadClass) {
        try {
            return (BaseManager)loadClass.getConstructor(new Class[]{}).newInstance(new Object[]{});
        } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
                | NoSuchMethodException | SecurityException e) {
            e.printStackTrace();
        }
        return null;
    }
}

6.编写MsgHandler类

package com.myimooc.classloader;

/**
 * @title 后台线程
 * @describe 后台启动一条线程不断刷新加载实现了热加载的类
 * @author zc
 * @version 1.0 2017-12-01
 */
public class MsgHandler implements Runnable{

    @Override
    public void run() {
        while(true){
            BaseManager manager = ManagerFactory.getManager(ManagerFactory.MY_MANAGER);
            manager.logic();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

7.编写ClassLoaderTest类

package com.myimooc.classloader;

/**
 * @title 测试类
 * @describe 测试Java类的热加载
 * @author zc
 * @version 1.0 2017-12-01
 */
public class ClassLoaderTest {
    
    public static void main(String[] args) {
        new Thread(new MsgHandler()).start();
    }
}

第四章:项目演示

4-1 简单介绍

Spring Boot简单介绍

是一个全新框架,目的是简化Spring应用的搭建与开发过程
该框架开发人员不需要定义样板化的配置
从根本上讲,是一些库的集合,构建项目,无须自行管理这些库的版本

Spring Boot特点

创建独立的Spring应用程序
嵌入的Tomcat,无须部署war文件
简化Maven配置和Gradle配置
自动配置Spring
提供生产就绪功能,如指标、健康检查和外部配置

Spring Boot使用场景

开发Restful风格的微服务架构
微服务、自动化、横向扩展
精简配置与整合其他工具

4-2 项目搭建

创建名为hotdeploy的gradle工程build.gradle脚本如下

buildscript {
    ext {
        springBootVersion = '1.5.6.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'

group = 'com.myimooc'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
    maven{ url "http://maven.aliyun.com/nexus/content/groups/public/"}
    mavenCentral()
}

dependencies {
    compile('org.springframework.boot:spring-boot-starter')
    compile('org.springframework.boot:spring-boot-starter-web')
    compile('org.springframework.boot:spring-boot-starter-data-jpa')
    compile('org.springframework.boot:spring-boot-starter-thymeleaf')
    compile('org.springframework.boot:spring-boot-devtools')
    compile('com.h2database:h2')
    
    testCompile('org.springframework.boot:spring-boot-starter-test')
}

代码编写

1.编写HotDeployApplication类

package com.myimooc.hotdeploy;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;

/**
 * @title Spring Boot 热启动
 * @describe 启动类
 * @author zc
 * @version 1.0 2017-12-01
 */
@SpringBootApplication
public class HotDeployApplication extends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication.run(HotDeployApplication.class, args);
    }
    
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(HotDeployApplication.class);
    }
}

2.编写HotDeployController类

package com.myimooc.hotdeploy.web.controller;

import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

/**
 * @title 控制器
 * @describe 
 * @author zc
 * @version 1.0 2017-12-01
 */
@Controller
public class HotDeployController {
    
    // 等价于 @RequestMapping(value="/say",method=RequestMethod.GET)
    @GetMapping("/say")
    public String say(HttpServletRequest request){
        request.setAttribute("say", "Hello Spring Boot!");
        return "index";
    }
}

3.编写index.html页面

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Index</title>
</head>
<body>

<span th:text="${say}"></span>

</body>
</html>

4-3 部署实现

Spring Boot热部署实现的方式

使用Spring Loaded:1.添加依赖,2.设置JVM参数
使用spring-boot-devtools:1.添加依赖

4-4 项目发布

发布方式

构建jar包,命令行运行Spring Boot程序
构建war包,发布到Tomcat

第五章:课程总结

5-1 课程总结

课程总结

课程介绍
热部署与热加载
热部署原理解析
Java类热加载案例分析
Spring Boot简单介绍
Spring Boot项目搭建
Spring Boot项目构建过程解析
Spring Boot热部署的实现
Spring Boot发布方式

妙手空空
1.3k 声望368 粉丝

博观而约取,厚积而薄发